#include "gamescene.hpp"

namespace VN{
    Chapter::Pack::Pack(Logic logic,const vector<TextMod>* l):text(l),logic(logic){}
    ResUnit::~ResUnit(){
        for(auto& n:res)SDL_DestroyTexture(n.src);
    }
    ResUnit::ResUnit(Renderer* r,MFPack& p,const char* dir){
        auto d=p.GetDir(dir);
        for(auto& n:d->file){
            auto s=n.first;
            auto item=s.begin(),end=s.end();
            while(item!=end){
                if(*item=='.'){
                    break;
                }
                item++;
            }
            auto d=n.second;
            auto t=r->LoadTextureRW(d->data(),d->size());
            SDL_SetTextureBlendMode(t,SDL_BLENDMODE_BLEND);
            res_map.emplace(s.substr(0,item-s.begin()),res.size());
            res.push_back(ResPair{t,false});
        }
    }
    Chapter::Chapter(initializer_list<const vector<TextLabel>*> c){
        unordered_map<string,size_t> index;
        for(auto l:c){
            for(auto& n:*l){
                for(auto& lang:n.content)content.emplace(lang.language,vector<Pack>()),index.emplace(lang.language,0);
            }
        }
        for(auto l:c){
            for(auto& n:*l)label.emplace(n.name,index);
        }
        for(auto l:c){
            for(auto& n:*l){
                auto& lf=label.find(n.name)->second;
                for(auto& lang:n.content){
                    auto& r=lang.sentence;
                    auto f=index.find(lang.language);
                    lf.find(lang.language)->second=f->second;
                    f->second+=r.size();
                    auto c=&content.find(lang.language)->second;
                    for(auto& v:r){
                        c->emplace_back(v.logic,&v.text);
                    }
                }
            }
        }
    }
    void NameImage::SetName(const vector<TextMod>& name){
        cache=name;
        _name=BuildText(name,default_font->_font);
        SDL_QueryTexture(_name,0,0,&_rect.w,&_rect.h);
        _rect.x=_area.x+(_area.w>>1)-(_rect.w>>1);
        _rect.y=_area.y+(_area.h>>1)-(_rect.h>>1);
        _visible=true;
    }
    void NameImage::SetName(const NameUnit& n){
        auto& name=n.find(g->CurrentLanguage())->second;
        cache=name;
        _name=BuildText(name,default_font->_font);
        SDL_QueryTexture(_name,0,0,&_rect.w,&_rect.h);
        _rect.x=_area.x+(_area.w>>1)-(_rect.w>>1);
        _rect.y=_area.y+(_area.h>>1)-(_rect.h>>1);
        _visible=true;
    }
    void NameImage::SetName(){
        cache.clear();
        if(_name)SDL_DestroyTexture(_name);
        _name=0;
        _visible=false;
    }
    NameImage::NameImage(GameScene* g,SDL_Rect& r,const char* back_path):
    Image((Window*)g->window(),r,back_path),g(g){
        _name=0;
        _visible=false;
    }
    NameImage::~NameImage(){
        if(_name)SDL_DestroyTexture(_name);
    }
    void NameImage::_draw(){
        auto r=_belong->renderer;
        r->DrawTexture(0,&_area,_t);
        if(_name)r->DrawTexture(0,&_rect,_name);
    }
    void LayerImage::QueryLayer(vector<BackInfo>& a){
        auto& layers=render;
        auto name=last.begin();
        auto layer=layers.begin();
        auto end=last.end();
        a.resize(last.size());
        auto n=a.begin();
        while(name!=end){
            n->length=strlen(*name);
            SDL_GetTextureAlphaMod(layer->s->src,&n->a);
            n->visible=layer->s->visible;
            n->name=new char[n->length+1]();
            memcpy(n->name,*name,n->length);
            name++,layer++,n++;
        }
    }
    void LayerImage::QueryLayer(FILE* fp){
        auto& layers=render;
        auto name=last.begin();
        auto layer=layers.begin();
        auto end=last.end();
        while(name!=end){
            auto l=strlen(*name);
            fwrite(&l,4,1,fp);
            Uint8 a;
            SDL_GetTextureAlphaMod(layer->s->src,&a);
            fwrite(&a,1,1,fp);
            fwrite(&layer->s->visible,1,1,fp);
            fwrite(*name,l,1,fp);
            name++,layer++;
        }
    }
    ResUnit* LayerImage::Res(){return unit;}
    LayerImage& LayerImage::PopLayer(size_t n){
        render.erase(render.end()-n,render.end());
        last.erase(last.end()-n,last.end());
        return *this;
    }
    size_t LayerImage::GetMemberNum(){
        return render.size();
    }
    LayerImage::LayerImage(Window* win,ResUnit* u,SDL_Rect& rect):
    Control(win),unit(u){
        _area=rect;
        angle=0;flip=SDL_FLIP_NONE;
        for(auto& n:u->res){
            SDL_SetTextureAlphaMod(n.src,0);
            n.visible=false;
        }
        alpha=0;
    }
    unsigned char LayerImage::Alpha(){return alpha;}
    LayerImage& LayerImage::ClearLayer(){render.clear(),last.clear();return *this;}
    LayerImage& LayerImage::AddLayer(initializer_list<const char*> name){
        auto& m=unit->res_map;
        auto& a=unit->res;
        for(auto& n:name){
            auto f=m.find(n);
            if(f!=m.end()){
                auto& t=a[f->second];
                int w,h;
                SDL_QueryTexture(t.src,0,0,&w,&h);
                render.emplace_back(RenderUnit{&t,SDL_Rect{0,0,w,h}});
                last.push_back(n);
            }
        }
        return *this;
    }
    LayerImage& LayerImage::AddLayer(vector<const char*>& name){
        auto& m=unit->res_map;
        auto& a=unit->res;
        for(auto& n:name){
            auto f=m.find(n);
            if(f!=m.end()){
                auto& t=a[f->second];
                int w,h;
                SDL_QueryTexture(t.src,0,0,&w,&h);
                render.emplace_back(RenderUnit{&t,SDL_Rect{0,0,w,h}});
                last.push_back(n);
            }
        }
        return *this;
    }
    LayerImage& LayerImage::SetLayerVisible(vector<const char*>& name,bool v){
        auto& m=unit->res_map;
        auto& a=unit->res;
        for(auto& n:name){
            auto f=m.find(n);
            if(f!=m.end()){
                a[f->second].visible=v;
            }
        }
        return *this;
    }
    LayerImage& LayerImage::SetLayerVisible(initializer_list<const char*> name,bool v){
        auto& m=unit->res_map;
        auto& a=unit->res;
        for(auto& n:name){
            auto f=m.find(n);
            if(f!=m.end()){
                a[f->second].visible=v;
            }
        }
        return *this;
    }
    LayerImage& LayerImage::SetAlpha(initializer_list<const char*> name,unsigned char v){
        auto& m=unit->res_map;
        auto& a=unit->res;
        for(auto& n:name){
            auto f=m.find(n);
            if(f!=m.end()){
                SDL_SetTextureAlphaMod(a[f->second].src,v);
            }
        }
        alpha=v;
        return *this;
    }
    LayerImage& LayerImage::SetAlpha(vector<const char*>& name,unsigned char v){
        auto& m=unit->res_map;
        auto& a=unit->res;
        for(auto& n:name){
            auto f=m.find(n);
            if(f!=m.end()){
                SDL_SetTextureAlphaMod(a[f->second].src,v);
            }
        }
        alpha=v;
        return *this;
    }
    LayerImage& LayerImage::Reset(){
        for(auto& n:unit->res)n.visible=false,SDL_SetTextureAlphaMod(n.src,0);
        return *this;
    }
    LayerImage& LayerImage::SetLayerVisible(bool v){
        for(auto& n:unit->res)n.visible=v;
        return *this;
    }
    LayerImage& LayerImage::SetAlpha(unsigned char a){
        for(auto& n:render)SDL_SetTextureAlphaMod(n.s->src,a);
        alpha=a;
        return *this;
    }
    LayerImage& LayerImage::SetAlpha(const char* name,unsigned char a){
        auto& layer_map=unit->res_map;
        auto f=layer_map.find(name);
        if(f!=layer_map.end()){
            SDL_SetTextureAlphaMod(unit->res.operator[](f->second).src,a);
            alpha=a;
        }
        return *this;
    }
    LayerImage::~LayerImage(){}
    void LayerImage::_draw(){
        auto r=_belong->renderer;
        auto& a=unit->res;
        for(auto& n:render){
            if(n.s->visible)r->DrawTexture(&n.r,&_area,n.s->src,angle,flip);
        }
    }
    LayerImage& LayerImage::ApplyChange(void(*f)(SDL_Rect&)){
        for(auto& n:render)f(n.r);
        return *this;
    }
    LayerImage& LayerImage::SetNewImage(ResUnit* u){
        render.clear();
        last.clear();
        unit=u;
        for(auto& n:u->res){
            SDL_SetTextureAlphaMod(n.src,0);
            n.visible=false;
        }
        return *this;
    }
    void LayerImage::SetPosition(int x,int y){
        _area.x=x,_area.y=y;
    }
    void LayerImage::OffsetPosition(int x,int y){
        _area.x+=x,_area.y+=y;
    }
    void LayerImage::SetSize(int w,int h){
        _area.w=w,_area.h=h;
    }

    void SceneState::Load(FILE* fp){
        unsigned int nn,nt,nm,nc,nb;
        fread(&nn,4,1,fp);
        fread(&nt,4,1,fp);
        fread(&nm,4,1,fp);
        fread(&nc,4,1,fp);
        fread(&nb,4,1,fp);
        fread(&index,4,1,fp);
        fread(&ch,4,1,fp);
        total=28+nm;
        music.resize(nm+1,0);
        fread((void*)music.data(),nm,1,fp);
        content.resize(nt);
        character.resize(nc);
        back.resize(nb);
        name.resize(nn);
        for(auto& m:name){
            fread(&nn,4,1,fp);
            fread(&m.color,4,1,fp);
            fread(&m.style,4,1,fp);
            fread(&m.hint,4,1,fp);
            fread(&m.scale,8,1,fp);
            auto s=new char[nn+1]();
            fread(s,nn,1,fp);
            cache.push_back(s);
            m.text=s;
            m.new_line=false;
            total+=24+nn;
        }
        for(auto& m:content){
            fread(&nn,4,1,fp);
            fread(&m.color,4,1,fp);
            fread(&m.style,4,1,fp);
            fread(&m.hint,4,1,fp);
            fread(&m.new_line,1,1,fp);
            fread(&m.scale,8,1,fp);
            auto s=new char[nn+1]();
            fread(s,nn,1,fp);
            cache.push_back(s);
            m.text=s;
            total+=25+nn;
        }
        for(auto& m:back){
            fread(&m.length,4,1,fp);
            fread(&m.a,1,1,fp);
            fread(&m.visible,1,1,fp);
            auto s=new char[m.length+1]();
            fread(s,m.length,1,fp);
            cache.push_back(s);
            m.name=s;
            total+=6+m.length;
        }
        for(auto& n:character){
            fread(&nn,4,1,fp);
            fread(&nt,4,1,fp);
            fread(&n.rect,sizeof(SDL_Rect),1,fp);
            auto s=new char[nn+1]();
            fread(s,nn,1,fp);
            n.name=s;
            n.ch.resize(nt);
            for(auto& m:n.ch){
                fread(&m.length,4,1,fp);
                fread(&m.a,1,1,fp);
                fread(&m.visible,1,1,fp);
                s=new char[m.length+1]();
                fread(s,m.length,1,fp);
                cache.push_back(s);
                m.name=s;
                total+=6+m.length;
            }
            total+=(8+sizeof(SDL_Rect))+nn;
        }
    }
    void SceneState::Load(GameScene* g){
        g->Redirect(ch,index);
        if(name.empty())g->name->SetVisible(false);else{
            g->name->SetName(name);
            g->name->SetVisible(true);
        }
        g->dialog->SetText(content);
        auto game_back=g->game_back;
        game_back->ClearLayer();
        for(auto& m:back){
            auto s=m.name;
            game_back->AddLayer({s}).SetLayerVisible({s},m.visible).SetAlpha(s,m.a);
        }
        for(auto& k:character){
            auto c=g->AddCharacter(k.name,{},k.rect);
            for(auto& m:k.ch){
                auto s=m.name;
                c->AddLayer({s}).SetLayerVisible({s},m.visible).SetAlpha(s,m.a);
            }
        }
    }
    SceneState::SceneState(){total=0;}
    SceneState::SceneState(const SceneState& v):music(v.music),index(v.index),
    content(v.content),character(v.character),name(v.name),total(v.total){
        for(auto& n:content){
            auto l=strlen(n.text);
            auto s=new char[l+1]();
            memcpy(s,n.text,l);
            cache.push_back(s);
            n.text=s;
        }
        for(auto& n:name){
            auto l=strlen(n.text);
            auto s=new char[l+1]();
            memcpy(s,n.text,l);
            cache.push_back(s);
            n.text=s;
        }
        for(auto& n:back){
            auto l=strlen(n.name);
            auto s=new char[l+1]();
            memcpy(s,n.name,l);
            cache.push_back(s);
            n.name=s;
        }
        for(auto& m:character){
            auto l=strlen(m.name);
            auto s=new char[l+1]();
            memcpy(s,m.name,l);
            m.name=s;
            for(auto& n:m.ch){
                l=strlen(n.name);
                s=new char[l+1]();
                memcpy(s,n.name,l);
                cache.push_back(s);
                n.name=s;
            }
        }
    }

    void SceneState::Write(FILE* fp){
        auto l=name.size();
        fwrite(&l,4,1,fp);
        l=content.size();
        fwrite(&l,4,1,fp);
        l=music.length();
        fwrite(&l,4,1,fp);
        l=character.size();
        fwrite(&l,4,1,fp);
        l=back.size();
        fwrite(&l,4,1,fp);
        fwrite(&index,4,1,fp);
        fwrite(&ch,4,1,fp);
        fwrite(music.c_str(),music.length(),1,fp);
        total=28+music.length();
        for(auto& m:name){
            l=strlen(m.text);
            fwrite(&l,4,1,fp);
            fwrite(&m.color,4,1,fp);
            fwrite(&m.style,4,1,fp);
            fwrite(&m.hint,4,1,fp);
            fwrite(&m.scale,8,1,fp);
            fwrite(m.text,l,1,fp);
            total+=24+l;
        }
        for(auto& m:content){
            l=strlen(m.text);
            fwrite(&l,4,1,fp);
            fwrite(&m.color,4,1,fp);
            fwrite(&m.style,4,1,fp);
            fwrite(&m.hint,4,1,fp);
            fwrite(&m.new_line,1,1,fp);
            fwrite(&m.scale,8,1,fp);
            fwrite(m.text,l,1,fp);
            total+=25+l;
        }
        for(auto& m:back){
            fwrite(&m.length,4,1,fp);
            fwrite(&m.a,1,1,fp);
            fwrite(&m.visible,1,1,fp);
            fwrite(m.name,m.length,1,fp);
            total+=6+m.length;
        }
        for(auto& item:character){
            auto name=item.name;
            auto nl=strlen(name);
            fwrite(&nl,4,1,fp);
            auto m=item.ch;
            l=m.size();
            fwrite(&l,4,1,fp);
            fwrite(&item.rect,sizeof(SDL_Rect),1,fp);
            fwrite(name,nl,1,fp);
            for(auto& m:item.ch){
                fwrite(&m.length,4,1,fp);
                fwrite(&m.a,1,1,fp);
                fwrite(&m.visible,1,1,fp);
                fwrite(m.name,m.length,1,fp);
                total+=6+m.length;
            }
            total+=(8+sizeof(SDL_Rect))+nl;
        }
    }
    CharacterInfo::~CharacterInfo(){
        delete[] name;
    }
}